Skip to content

docs(diagrams): add Archify SVG exporter with 4.5:1 contrast check - #1498

Merged
mergify[bot] merged 2 commits into
mainfrom
claude/fabrik-ds-1a-export-svg
Oct 15, 2026
Merged

mergify[bot] merged 2 commits into
mainfrom
claude/fabrik-ds-1a-export-svg

Conversation

@Cuarroc

@Cuarroc Cuarroc commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Dieses Paket legt das Werkzeug docs/diagrams/export-svg.mjs ins Repo: Es macht aus einem Archify-Diagramm ein festes, helles oder dunkles SVG und misst im Modus contrast jede Textfarbe gegen den wirklich gemalten Hintergrund. Sobald irgendein Text unter dem Verhältnis 4,5:1 liegt, endet der Modus mit Fehlercode 1, damit schlecht lesbare Diagramme auffallen, bevor sie ins Fabrik-Designsystem wandern. Du musst nichts entscheiden; die fünf angenommenen Befunde der ersten Prüfrunde sind behoben, und der PR bleibt ein Entwurf, bis ein Prüfer außerhalb der Anthropic-Familie (Stufe B) auch die Änderungen daran gesehen hat.

Report

Package: FABRIK-DS-1 PR A (Fabrik Stufe 1, Diagrammquellen; Ziel G-V2) · Review tier: B
Autor: UI-Designer (Claude Opus), committed by server job
Goal / benefit: export-svg.mjs und export-svg.test.mjs liegen unter docs/diagrams/; der Exporter erzeugt statische Light/Dark-SVGs und prüft Kontrast. · Parent / owner: G-V2 Punkt 3/7, Root als Chief (Auftrag 15.10.2026)
Scope / limits: nur diese zwei Dateien, kein README, kein App-Code, keine Seam. 236 Diff-Zeilen gegen die Merge-Base (Limit 300; 199 vor der Nachbesserung).

What changed

  • docs/diagrams/export-svg.mjs (156 Zeilen): Modus export <in.html> <out.svg> <light|dark> "<title>" "<desc>" tauscht die Archify-Palette gegen die ProjectA-Tokens, inlined alle berechneten Farben, entfernt die Viewer-Schicht und beschneidet auf die Zeichnung. Modus contrast <file.svg>... gibt je Farbpaar das WCAG-Verhältnis aus und setzt process.exitCode = 1, wenn das Minimum unter 4,5 liegt. Chromium kommt aus ARCHIFY_CHROME, sonst aus Playwright.
  • docs/diagrams/export-svg.test.mjs (43 Zeilen): zwei Tests auf einzeiligen SVG-Fixtures. #999999 auf #FFFFFF (2,85:1) muss Exit 1 liefern, #121826 auf #E9EDF3 (15,09:1) Exit 0. Ohne Chromium überspringen sich die Tests mit Begründung.
  • Erste Fassung (5d3eed8f): beide Dateien byte-identisch zur Vorlage des UI-Designers (cmp Exit 0). Nachbesserung (0665ec44): die fünf angenommenen Befunde der Prüfrunde 1 (Tabelle unten); die Logik des Exports ist unverändert, nur Start, Pfadbildung und Aufräumen. Die try/finally-Blöcke rücken zwei Browser-Blöcke ein; git diff -w zeigt die eigentliche Änderung.
  • Keine Abhängigkeit hinzugefügt: playwright-core wird aus dem vorhandenen node_modules aufgelöst (kommt über @playwright/test, steht nicht direkt in package.json).

Evidence

Command Exit code
npm run dev:start-check (RAM 10,7 GB frei, 1 fremder Cargo-Build) 0
node --test docs/diagrams/export-svg.test.mjs (lief, 2 von 2 bestanden, 0 übersprungen, Chromium aus Playwright-Cache) 0
Mutation: Zeile 149 min < 4.5 → min < 2, dann derselbe Test: Test 1 rot (0 !== 1), Test 2 grün 1
Mutation zurückgesetzt, cmp gegen Vorlage; Test erneut 0 / 0
bash scripts/ci/red-first.sh (Merge-Base: Datei fehlt → rot/fehlend, Kopf: ✔ contrast fails text below 4.5:1 with exit 1) 0
git diff --check origin/main HEAD 0
bash scripts/ci/gates.sh lane prepush (26 Gates grün, 453 s, ungepiped, HEAD 5d3eed8f) 0
git push -u origin claude/fabrik-ds-1a-export-svg (Pre-Push-Hook führt die Prepush-Bahn erneut aus) 0
git ls-remote origin claude/fabrik-ds-1a-export-svg = 5d3eed8f22ca818b38e997deb1821d783cf880d0 = lokaler HEAD 0

Architecture drift: architecture-drift grün, nichts gemeldet, nichts auf die Allowlist gesetzt.

NICHT ABGEDECKT

  • Windows-Bahn: nicht gelaufen (Linux-Server); sie kommt aus der Merge-Queue. Der Exporter ist plattformneutral, aber die Schriftmessung unterscheidet sich je Betriebssystem (laut Übergabenotiz verschieben sich bei architektur-light/-dark Zuschnitt und Badge-x um ≤ 4 px bzw. ≤ 0,07 px); das betrifft nur den Modus export.
  • Modus export ist nur dünn getestet. Der neue Test R1498-G1 lässt export auf einem winzigen Fixture mit #/%/Leerzeichen im Pfad laufen und prüft nur Exit 0 und den Text im SVG; Palette, Viewer-Schicht und Zuschnitt prüft nichts (Nachfolger R1498-G5). Die echte Ausgabe liegt erst mit den Diagramm-PRs B–F vor.
  • Kein Gate führt docs/diagrams/*.test.mjs aus. Weder npm test noch die Prepush-Bahn kennen den Pfad; bewiesen wird der Test nur über das Test-First-Trailer-Gate red-first. Der zweite Test (contrast passes ink on ground with exit 0) hat keinen Trailer und läuft deshalb in keinem Gate; ihn zu verdrahten wäre ein eigenes Paket. Die beiden neuen Tests der Nachbesserung tragen Trailer.
  • Chromium in CI: Lokal lief der Test. Dass gates (linux) ihn ebenfalls ausführt und nicht überspringt, folgt aus .github/actions/setup-linux (installiert Chromium standardmäßig), ist aber in einem CI-Lauf noch nicht beobachtet. Ein Überspringen würde red-first rot machen (fail-closed, keine ✔-Zeile), nicht still grün.
  • Archify-Credit (MIT, tt-a1i): Die beiden Dateien tragen keinen Urheberhinweis, es gab keinen zu übernehmen und ich habe keinen erfunden. Falls ein Hinweis in den Dateien selbst gewünscht ist, wäre das eine Entscheidung für Root; der natürliche Ort ist das Diagramm-README (PR D).
  • Lizenzen: keine neuen Abhängigkeiten, nichts zu prüfen.

Reviews and disposition

Runde 1: google/gemini-3.8-flash @5d3eed8f (Stufe B); Annahme durch Root am 15.10.2026. Review der Nachbesserung (Delta 5d3eed8f..0665ec44): ausstehend (Stufe B), beauftragt durch Root.

Review disposition (round 1)

ID Source Severity Finding Disposition
R1498-G1 google/gemini-3.8-flash @5d3eed8f high export-svg.mjs:48 baut die URL als 'file://' + resolve(inHtml); ein #, %, Leerzeichen oder Windows-Laufwerk im Pfad ergibt eine falsche URL. accepted, 0665ec4: pathToFileURL(resolve(inHtml)).href; keine weitere file://-Verkettung in beiden Dateien (grep). Test export opens an HTML file whose path needs URL escaping: rot (ERR_FILE_NOT_FOUND), jetzt grün. Auch auf Linux testbar (# im Pfad), nicht nur unter Windows.
R1498-G2 google/gemini-3.8-flash @5d3eed8f medium export-svg.mjs:17 und export-svg.test.mjs:17 lösen playwright-core über createRequire(resolve('package.json')) auf, also je nach Arbeitsverzeichnis. accepted, 0665ec4: createRequire(import.meta.url) in beiden Dateien. Test contrast finds playwright-core from a working directory outside the repository: rot (Cannot find module 'playwright-core'), jetzt grün. Abweichung vom Auftrag: mit Arbeitsverzeichnis docs/diagrams war der alte Code bereits grün (Node sucht node_modules aufwärts), rot wird er erst außerhalb des Repos; der Test nutzt deshalb das Temp-Verzeichnis.
R1498-G3 google/gemini-3.8-flash @5d3eed8f medium Der Browser (:46, :124) wird nicht geschlossen, wenn goto/setContent/evaluate wirft. accepted, 0665ec4: try/finally mit await browser.close() in exportSvg und contrast. Kein Test: ein ausgelaufener Browser stirbt mit seinem Elternprozess, das ist von außen nicht beobachtbar.
R1498-G4 google/gemini-3.8-flash @5d3eed8f medium chromiumMissing() (export-svg.test.mjs:18) startet ohne das --no-sandbox aus ARCHIFY_NO_SANDBOX, überspringt also dort, wo das Skript liefe. accepted, 0665ec4: gleiche args wie launch(). Kein Test: braucht einen Rechner ohne nutzbare Sandbox.
R1498-G5 google/gemini-3.8-flash @5d3eed8f – Kein Test für den Modus export mit Fixture. follow-up (nicht in diesem PR); der G1-Test deckt nur Pfad und Schreiben ab, nicht den Inhalt.
R1498-G6 google/gemini-3.8-flash @5d3eed8f – Die Regex >\s+< (export-svg.mjs, Zeile out = svg.replace(...)) entfernt auch bedeutsame Leerräume. follow-up (nicht in diesem PR).
R1498-G7 google/gemini-3.8-flash @5d3eed8f low mkdtempSync-Verzeichnisse des Tests (:28) werden nie gelöscht. accepted, 0665ec4: rmSync(dir, { recursive: true, force: true }) im finally (Hilfsfunktion inScratch). Kein Test: ein übrig gebliebenes Temp-Verzeichnis wird nicht geprüft.

Test-First (plain text, for the red-first gate):

Test-First: docs/diagrams/export-svg.test.mjs::contrast finds playwright-core from a working directory outside the repository
Test-First: docs/diagrams/export-svg.test.mjs::export opens an HTML file whose path needs URL escaping
No-Test: R1498-G3, G4 and G7 are not observable on Linux (see table)

Größe: 236 Diff-Zeilen gegen die Merge-Base (164 + 72), unter dem Limit von 300; keine Überschreitung durch die Prüfrunde.

Evidence (Nachbesserung, HEAD 0665ec44)

Command Exit code
npm run dev:start-check (RAM 12,7 GB frei, 0 Cargo-Builds) 0
Neue Tests gegen das alte Skript (5d3eed8f): 2 von 4 rot (Cannot find module 'playwright-core', page.goto: net::ERR_FILE_NOT_FOUND), contrast-Tests 1 und 2 grün 1
node --test docs/diagrams/export-svg.test.mjs am Kopf (lief, 4 von 4 bestanden, 0 übersprungen, Chromium aus dem Playwright-Cache) 0
bash scripts/ci/red-first.sh (alle drei Trailer: an der Merge-Base rot/fehlend, am Kopf grün) 0
git diff --check 0
bash scripts/ci/gates.sh lane prepush (26 Gates grün, 267 s, ungepiped, HEAD 0665ec44) 0
git push -u origin claude/fabrik-ds-1a-export-svg (Pre-Push-Hook führt die Prepush-Bahn erneut aus; ohne Force) 0
git ls-remote origin claude/fabrik-ds-1a-export-svg = 0665ec447808950d49b7c79cb317301bf54cb53e = lokaler HEAD 0

Hinweis zum Belegwert von red-first: Die Datei existiert an der Merge-Base nicht, das Gate sieht jeden Test dort als „fehlend“. Dass die zwei neuen Tests gegen den alten Code rot sind, belegt der Lauf in Zeile 2.

Open points

  • State: implemented und tested (lokal, siehe beide Evidence-Tabellen); Runde 1 eingearbeitet, Delta-Review ausstehend; nicht accepted, nicht merged.
  • Handover: Kandidat 0665ec44 (vorher 5d3eed8f) auf Basis b9dfad49; Entwurf; Root beauftragt das Review und setzt später review-ok. Folgepakete B–F (Diagramme, README) liegen in docs/diagrams/ und docs/design/2026-10-fabrik/.

🤖 Generated with Claude Code

Review round 2 (Root)

Delta 5d3eed8f..0665ec44 reviewed by google/gemini-3.8-flash (Google, author family Anthropic): APPROVE, no new findings. PR size after the review-forced fix round: +236 (under 300). Follow-ups: R1498-G5, R1498-G6, and wiring docs/diagrams/*.test.mjs into a gate lane.

docs/diagrams/export-svg.mjs turns an Archify HTML diagram into a static,
self-contained light or dark SVG using the ProjectA design tokens. Its
`contrast` mode measures the WCAG ratio of every text colour against what is
really painted behind it and exits 1 when any pair is below 4.5:1.

The test drives contrast mode on one-line SVG fixtures: the WCAG reference
pair #999999 on #FFFFFF (2.85:1) must exit 1, ink on ground (15.09:1) must
exit 0. It skips with a reason when no Chromium is available.

Authored by the UI designer; committed by the server job.

Test-First: docs/diagrams/export-svg.test.mjs::contrast fails text below 4.5:1 with exit 1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
return new XMLSerializer().serializeToString(clone);
}, [T, FONT_SANS, FONT_MONO, title, desc]);
await browser.close();
const out = svg.replace(/<!--[\s\S]*?-->/g, '').replace(/>\s+</g, '><');
Review of PR #1498 at 5d3eed8 (google/gemini-3.8-flash) found five
defects in docs/diagrams/export-svg.mjs and its test:

- R1498-G1: page.goto built the URL as 'file://' + path, which breaks on
  a '#', '%' or space in the path (and on Windows drive paths). It now
  uses pathToFileURL(...).href.
- R1498-G2: playwright-core was resolved against the current directory
  (createRequire(resolve('package.json'))); outside the repository the
  script died with "Cannot find module". It now resolves against the
  script itself (import.meta.url), in the script and in the test.
- R1498-G3: the browser stayed open when goto/evaluate threw; export and
  contrast now close it in a finally block.
- R1498-G4: the test's Chromium probe ignored ARCHIFY_NO_SANDBOX, so it
  skipped where the script itself would run; it now uses the same
  launch arguments.
- R1498-G7: the tests left their mkdtemp directories behind; they are
  removed after each run.

Not fixed here (follow-up): G5 (export-mode assertions on a real
fixture) and G6 (the '>\s+<' whitespace regex).

The two new tests ran red against the previous script: the module was
not found from a foreign working directory, and Chromium answered
ERR_FILE_NOT_FOUND for a path containing '#'. The try/finally edits
re-indent the browser blocks; `git diff -w` shows the real change.

Test-First: docs/diagrams/export-svg.test.mjs::contrast finds playwright-core from a working directory outside the repository
Test-First: docs/diagrams/export-svg.test.mjs::export opens an HTML file whose path needs URL escaping
No-Test: R1498-G3, G4 and G7 are not observable on Linux: a leaked browser dies with its parent process, the sandbox-less probe needs a host without a usable sandbox, and a leftover temp directory is not asserted

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Cuarroc
Cuarroc marked this pull request as ready for review October 15, 2026 04:53
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-15T04:53:15.589293Z 0665ec4 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Cuarroc

Cuarroc commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

review-ok bound to head 0665ec44 (tier B, author family Anthropic; reviewer google/gemini-3.8-flash APPROVE rounds 1+2, round 2 at this head; gates linux/windows + red-first green; follow-ups R1498-G5/G6 recorded). @Mergifyio queue

@mergify mergify Bot added the queued label Oct 15, 2026
@mergify
mergify Bot merged commit 8ac3c4d into main Oct 15, 2026
19 of 20 checks passed
@mergify

mergify Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 11 minutes 16 seconds in the queue, including 11 minutes running CI.

Required conditions to merge
  • check-success = gates (linux)
  • check-success = gates (windows)
  • check-success = red-first
  • github-review-approved [🛡 GitHub repository ruleset rule main-pr-required-checks]
  • label != do-not-merge
  • any of [🛡 GitHub repository ruleset rule main-pr-required-checks]:
    • check-success = @github-actions/gates (linux)
    • check-neutral = @github-actions/gates (linux)
    • check-skipped = @github-actions/gates (linux)
  • any of [🛡 GitHub repository ruleset rule main-pr-required-checks]:
    • check-success = @github-actions/gates (windows)
    • check-neutral = @github-actions/gates (windows)
    • check-skipped = @github-actions/gates (windows)
  • any of [🛡 GitHub repository ruleset rule main-pr-required-checks]:
    • check-success = @github-actions/red-first
    • check-neutral = @github-actions/red-first
    • check-skipped = @github-actions/red-first
  • any of [🛡 GitHub repository ruleset rule main-pr-required-checks]:
    • check-neutral = @mergify/Mergify Merge Protections
    • check-skipped = @mergify/Mergify Merge Protections
    • check-success = @mergify/Mergify Merge Protections

@mergify
mergify Bot deleted the claude/fabrik-ds-1a-export-svg branch October 15, 2026 05:15
@mergify mergify Bot removed the queued label Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-ok Pipeline: review disposition complete, no open high finding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants